perm filename PRIVRN.DOC[HAL,HE]1 blob sn#116210 filedate 1974-08-16 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00006 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00002 00002	THIS FILE IS INTERNAL DOCUMENTATION NOT FOR PUBLICATION.
C00003 00003	    TRAJECTORIES  not yet ready for perusal
C00012 00004		IMPLEMENTATION OF ON-MONITORS
C00015 00005		INTERPRETER CODE FOR MOTIONS
C00017 00006		THE INTERPRETER SCHEDULER
C00026 ENDMK
C⊗;
THIS FILE IS INTERNAL DOCUMENTATION NOT FOR PUBLICATION.
Authors: RF and LOU.

    TRAJECTORIES  not yet ready for perusal

1)  Code is  executed  interpretatively to  set  up temporary  frames
necessary for this motion.

2)  The  "start move"  pseudo-op  is encountered;  it  points  to the
location of a vector of joint  start addresses (for 6 joints, need  a
vector of 12 adresses, because each joint needs both its location and
its inertia tables).

3)  A scan is made for each joint  down its chain of location tables,
during which the coefficients for each segment are modified  by a 5th
order  polynomial  to bring  them  into  conformity  with the  frames
specified, if necessary.

4) A  process is  sprouted for  each joint  which will  service  that
joint.   Actually, only  one copy  of that  process exists, and  each
joint  has its own  data area which  contains a)  servo constants for
that joint, b)  location counters into  the two  types of tables,  c)
precalculated values relevant to the next awakening.

5) Each process, on each awakening,  checks the clock to make sure it
was  awakened at the right time  (fatal error otherwise), outputs the
precalculated  drive,  decides how  long  it  is  willing  to  sleep,
reserves  an awakening  slot (more  on these  soon) in  the calendar,
computes the drive for that time, and goes to sleep.

6) As each joint finishes its whole trajectory, it requests that  the
monitor kill it.   When all the joints have been  killed, the monitor
(which  knows that  a move  was in progress)  returns control  to the
interpreter (which is itself a process) where it left off.


Termination  of a move is quite simple:  The joint status word, which
is global to the entire runtime, is just set for each joint  affected
to  a value  which  means "unrunnable"  The  next time  the servo  is
awakened,  it  will  notice  this   and  request  euthanasia.     The
termination also requires that the motors be  stopped, and the brakes
applied, and the resting point of the arm determined.  

Some care must be taken to prevent two interpreters proceeding at the
same time  under conditions whereby one should  really be waiting for
the other.    Specifically,  during  a  move,  the  interpreter  that
initiated that move is suspended until it is complete.  Therefore, if
an  on-test  causes  another  interpreter  to  start,  the  suspended
interpreter must remain suspended until the new one dies.
TRAJECTORY TABLES

The following are necessary properties of trajectory tables:
	Must state which frames are used, so the servo can do appropriate 
		update.
	Must be perspicuous enough to allow servo to find the things which
		it must update.
	Must allow escape to interpret mode at end of each segment for
		each joint, in order to initiate new motions or "on" tests.

	A trajectory table  is a set  of joint-segment tables.   Each
joint-segment table is intended to give instructions for the servoing
of one joint along one segment of  the path.  There are two types  of
such tables: location  and inertia.  The servo follows  each of these
simultaneously and asynchronously.


FORMAT OF THE  LOCATION-JOINT-SEGMENT TABLE: This table is  ten words
long.

words 1,2,3,4,5,6:  coefficients a5,a4,a3,a2,a1,a0 of the trajectory.
Polynomial is normalised for tε[0,1].

word 7: Duration of segment in milliseconds.

word 8: Pointer to next location-joint-segment table for  this joint.
If this is the last segment, word 8 is 0.

word  9: Pointer  to frame  which is  to be  assumed at  end of  this
segment.  If no particular frame, then this is  0.  This is used in a
preprocessing step to modify  the coefficients of the  polynomial; at
swing time it is not needed.

word 10:  If not 0,   then a  pointer to a  location of interpretable
code where an interpreter is to  be instantiated and begun.  This  is
for the THEN part of VIA lists.

FORMAT OF  THE INTERTIA-JOINT-SEGMENT TABLE:  This table  is 7  words
long.

words 1,2: Coefficients j1,j0 of joint inertia polynomial, normalized
for tε[0,1].

words  3,4:  Coefficients  g1,g0   of  gravity  loading   polynomial.
Likewise normalized.

word 5: Duration of segment in milliseconds.

word 6: Control word, with bits for free, force, drive, nodrive, etc.
see later  discussion on control words.  This is copied into a global
location at start of inertia-joint-segment.

word 7: Pointer to next intertia-joint-segment table  for this joint.
If this is the last segment, word 7 is 0.


GO TABLES

The following are necessary properties of go tables:
	Must state list of frames to use (including initial deproach
and via-list)
	Must allow escape to interpret mode at end of each segment.

	A GO table is a list of frames through which the motion is to
proceed.  Each is specified by its address.  The first one is the deproach
of the start frame; the penultimate is the deproach of the final
frame.  Following each frame address is an interpreter address, which
is the adrress of the interpreter (if any) to be invoked upon achievement
of the preceeding frame.  The last frame is distinguished by an interpreter
address of -1.
	IMPLEMENTATION OF ON-MONITORS
This page is a set of notes for the eventual documentation.
Author:  RF

	The code for an on-monitor is in-line and jumped around.
The code is PDP11 code, including calls on arithmetic subroutines,
if neccessary.

Each on-monitor is given a unique name by the compiler.
The scheduler associates that name with the location of the
enable bit for this monitor.

On-monitors have only two states:  enabled and disabled.
There is a status word associated with each on-monitor;
it contains two bits:  the enabled, and the kill.

At the start of a program, all on-monitors are disabled.
That means enable=0, kill=0.

At the end of a block, the compiler includes code to disable all
on-monitors within that block.

Only one copy of an on-monitor ever exists.  No multiple instantiations.

ENABLE <NAME>
To enable a software monitor, 
tell kernel to activate it at its address, in on-level, where
it will have PDP11 code to:
1) look at its "enabled bit".  If already on, dismiss. If not, turn on.
2) clear all its working buffers (if any) 
3) schedule its second entry (the monitor itself) to be awakened at
the appropriate intervals.  Then dismiss.

The software monitor, every time it is awakened, does the following:
1)  look at its "kill bit".  If on, clear and dismiss.
2)  execute the code for its check.
3)  if condition satisfied, do immediate stuff,
 instantiate the conclusion, and dismiss.
4)  reschedule self at appropriate interval.  Dismiss.

To enable a hardware monitor,
tell kernel to activate it at its address, in on-level, where
it will have PDP11 code to:
1)  look at the device enabled bit.  If already on, return.  If not, turn on.
2)  dismiss.

DISABLE <NAME>
To disable a software monitor, set its  "kill bit".
To disable a hardware monitor, clear its "enabled bit".
	INTERPRETER CODE FOR MOTIONS
not yet ready.  Author:  RF.

The code generated for a move includes the following interpreter commands:

1) Prepare move.  This points to the move table, and causes the trajectory
to be modified to conform to current locations of via points.  Joint
loading is calculated for each via point as well.

2) Enable on-monitor.  There may be more than one of these instructions.

3) Start motion.  This points to the modified move table.

4) Await completion.  This causes the interpreter to wait until the
move has completed, or the arm has been stopped for some reason.
The signal which awakes the interpreter comes from whichever joint
servo finishes last: each servo, as it finishes, checks to see if all
the others are done, and if so, awakes the interpreter at servo level.

5) disable all on-monitors associated with move.  This is done at
servo level

6) dismiss to interpreter level.
	THE INTERPRETER SCHEDULER

	The interpreter scheduler is a set of routines used for
handling interpreter sprouting, termination, waiting, and scheduling,
and for on-monitor enabling and disabling.  The code "↑" means
switch to high priority (above any on-monitor); the code "↓" means
return to previous priority.

RCLASS ONID (INTEGER STATUS, OPC; RPTR (ONID) NEXT);
	COMMENT:  STATUS holds the Kill and Enable bits.
	OPC is the On-program counter.
	NEXT is used for chaining of ons;

RCLASS PROCESSID (INTEGER PC, DEPENDENTS, WAITTYPE;  
	RPTR(PROCESSID) NEXT, MOTHER; RPTR(ONID) RUNONS, WAITONS);
	COMMENT:  PC=program counter.  DEPENDENTS=count.
	WAITTYPE=0 for run, 1 for dependent wait, 2 for event wait.
	NEXT used for queuing.  MOTHER points to mother.
	RUNONS is list of dependent on-monitors.
	WAITONS is list of dependent on-monitors active only during wait;

RCLASS QUEUE (RPTR(PROCESSID) HEAD, TAIL);
RPTR (PROCESSID) CURRENT;
RPTR(QUEUE) RUNQUEUE;
DEFINE ENABLE = "'1";
DEFINE KILL = "'2";

PROCEDURE INITIALIZE;
	BEGIN
	QUEUE:HEAD(RUNQUEUE) ← NEW_RECORD (PROCESSID);
	FOREACH ON-MONITOR DO
		BEGIN
		X ← NEW_RECORD (ONID);
		ONID:NEXT(X) ← RNULL;
		ONID:STATUS(X) ← 0;
		ONID:OPC(X) ← OPC;
		END;
	END;

PROCEDURE ENQUEUE(RPTR(QUEUE) WHERE; RPTR(PROCESSID) NEW);
	BEGIN
	COMMENT:  Puts a process on a queue.  QUEUE tells which
		queue.  NEW is the process id;
	QUEUE:TAIL(WHERE) ← PROCESSID:NEXT(QUEUE:TAIL(WHERE)) ← NEW;
	PROCESSID:NEXT(QUEUE:TAIL(WHERE)) ← RNULL;
	END;

RPTR(PROCESSID) PROCEDURE DEQUEUE (RPTR(QUEUE) WHERE);
	BEGIN
	COMMENT:  Returns head of queue, which is removed;
	RPTR(PROCESSID) ANSWER;
	IF QUEUE:HEAD(WHERE) = QUEUE:TAIL(WHERE) 
	THEN	RETURN(RNULL);  COMMENT:  Empty queue;
	ANSWER ← PROCESSID:NEXT(QUEUE:HEAD(WHERE));
	IF (PROCESSID:NEXT(QUEUE:HEAD(WHERE)) ← PROCESSID:NEXT(ANSWER)) = RNULL
	THEN	QUEUE:TAIL(WHERE) ← QUEUE:HEAD(WHERE);
	RETURN(ANSWER);
	END;

PROCEDURE SPROUT (IPC);
	BEGIN
	COMMENT: IPC is the interpreter program counter.
	The effect is to put the new interpreter in the run-queue;
	RPTR (PROCESSID) NEW;
	↑;
	NEW ← NEW_RECORD (PROCESSID);
	PROCESSID:PC(NEW) ← IPC;
	PROCESSID:DEPENDENTS(NEW) ← 0;
	PROCESSID:WAITTYPE(NEW) ← 0;
	PROCESSID:MOTHER(NEW) ← CURRENT;
	PROCESSID:DEPENDENTS(CURRENT) ← PROCESSID:DEPENDENTS(CURRENT) + 1
	ENQUEUE(RUNQUEUE,NEW);
	↓;
	END;

PROCEDURE TERMINATE;
	BEGIN  COMMENT:  Terminates the caller;
	RPTR(PROCESSID) MOM;
	RPTR(ONID) VICTIM, OLD;
	INTEGER TEMP;
	↑;
	IF PROCESSID:DEPENDENTS(CURRENT) ≠ 0
	THEN 	BEGIN  COMMENT:  This should never happen;
		ERROR("ACTIVE PROCESS DEPENDS ON BLOCK BEING EXITED");
		WAIT;
		END;
	MOM ← PROCESSID:MOTHER(CURRENT);
	TEMP ← PROCESSID:DEPENDENTS(MOM) ← PROCESSID:DEPENENTS(MOM) - 1;
	IF PROCESSID:WAITTYPE(MOM)=1 AND TEMP=0
	THEN	BEGIN  COMMENT:  Must awaken mom;
		VICTIM ← PROCESSID:WAITONS(MOM);
		PROCESSID:WAITONS(MOM) ← RNULL;
		WHILE VICTIM ≠ RNULL DO
			BEGIN  COMMENT:  Kill mom's wait ons;
			DISABLE(VICTIM);
			OLD ← VICTIM;
			VICTIM ← ONID:NEXT(VICTIM);
			ONID:NEXT(OLD) ← RNULL;
			END;
		PROCESSID:WAITTYPE(MOM) ← 0;
		ENQUEUE(RUNQUEUE,MOM);
		END;
	VICTIM ← PROCESSID:RUNONS(CURRENT);
	PROCESSID:RUNONS(CURRENT) ← RNULL;
	WHILE VICTIM ≠ RNULL DO
		BEGIN  COMMENT:  Kill current's run ons;
		DISABLE(VICTIM);
		OLD ← VICTIM;
		VICTIM ← ONID:NEXT(VICTIM);
		ONID:NEXT(OLD) ← RNULL;
		END;
	CURRENT ← RNULL;  COMMENT:  explicit deallocate?;
	↓;
	JRST RESCHEDULE;
	END;

PROCEDURE WAIT;
	BEGIN  COMMENT: Caller wishes to wait until all descendents have dismissed;
	RPTR(ONID) DEB;
	↑;
	IF PROCESSID:DEPENDENTS(CURRENT) ≠ 0
	THEN	BEGIN  COMMENT:  Must wait;
		PROCESSID:PC(CURRENT) ← <RETURN ADDRESS>;
		PROCESSID:WAITTYPE(CURRENT) ← 1;
		DEB ← PROCESSID:WAITONS(CURRENT);
		WHILE DEB ≠ RNULL DO
			BEGIN  COMMENT:  Enable all wait ons;
			IF ONID:STATUS(DEB) LAND ENABLE = 0
			THEN	BEGIN
				ONID:STATUS(DEB) ← ONID:STATUS(DEB) LAND ¬KILL;
				PUSHJ (ONID:OPC(DEB));
				END;
			DEB ← NEXT(DEB);
			END;
		↓;
		JRST RESCHEDULE;
		END
	ELSE ↓;
	END;

PROCEDURE RUN_ENABLE (RPTR (ONID) MON);
	BEGIN  COMMENT:  Wish to enable a run on monitor;
	↑;
	IF ONID:STATUS(MON) LAND ENABLE ≠ 0
	THEN	BEGIN ↓; RETURN END;  COMMENT:  Already going;
	ONID:STATUS(MON) ← ONID:STATUS(MON) LAND ¬KILL;
	IF ONID:NEXT(MON) = RNULL 
	THEN	BEGIN  COMMENT:  Add this run on to list for Current;
		ONID:NEXT(MON) ← PROCESSID:RUNONS(CURRENT);
		PROCESSID:RUNONS(CURRENT) ← ONID;
		END;
	PUSHJ (ONID:OPC(MON));
	↓;
	END;

PROCEDURE WAIT_ENABLE (RPTR (ONID) MON);
	BEGIN  COMMENT:  Wish to set up a wait on monitor;
	↑;
	IF ONID:STATUS(MON) LAND ENABLE ≠ 0
	THEN	BEGIN ↓; RETURN END;  COMMENT:  Already going;
	IF ONID:NEXT(MON) = RNULL 
	THEN	BEGIN  COMMENT:  Add this wait on to list for Current;
		ONID:NEXT(MON) ← PROCESSID:WAITONS(CURRENT);
		PROCESSID:WAITONS(CURRENT) ← ONID;
		END;
	↓;
	END;

PROCEDURE DISABLE (RPTR(ONID) MON);
	BEGIN  
	ONID:STATUS(MON) ← KILL;
	END;

PROCEDURE RESCHEDULE;
	BEGIN
	↑;
	IF CURRENT = RNULL 
	THEN 	BEGIN
		WHILE RUNQUEUE = RNULL DO BEGIN ↓; ↑; END;
		CURRENT ← DEQUEUE(RUNQUEUE);
		PUSH NAME(CURRENT);
		PUSH IPC(CURRENT);
		↓;
		PUSHJ INTERPRETER;
		END;
	ELSE	↓;
	END;